/* Copyright (c) 2007 Scott Lembcke
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <string.h>

#include "GL/glew.h"
#include "GL/glfw.h"

#include "chipmunk/chipmunk_private.h"
#include "ChipmunkDemo.h"
#include "ChipmunkDemoShaderSupport.h"
#include "ChipmunkDemoTextSupport.h"

#include "VeraMoBd.ttf_sdf.h"

//#define Scale 3.0f
#define Scale 0.70f
#define LineHeight (18.0f*Scale)

static GLuint program;
static GLuint texture;

struct v2f {GLfloat x, y;};
typedef struct Vertex {struct v2f vertex, tex_coord; cpSpaceDebugColor color;} Vertex;
typedef struct Triangle {Vertex a, b, c;} Triangle;

static GLuint vao = 0;
static GLuint vbo = 0;

// char -> glyph indexes generated by the lonesock tool.
static int glyph_indexes[256];

void
ChipmunkDemoTextInit(void)
{
	GLint vshader = CompileShader(GL_VERTEX_SHADER, GLSL(
		attribute vec2 vertex;
		attribute vec2 tex_coord;
		attribute vec4 color;
		
		varying vec2 v_tex_coord;
		varying vec4 v_color;
		
		void main(void){
			// TODO: get rid of the GL 2.x matrix bit eventually?
			gl_Position = gl_ModelViewProjectionMatrix*vec4(vertex, 0.0, 1.0);
			
			v_color = color;
			v_tex_coord = tex_coord;
		}
	));
	
	GLint fshader = CompileShader(GL_FRAGMENT_SHADER, GLSL(
		uniform sampler2D u_texture;
		
		varying vec2 v_tex_coord;
		varying vec4 v_color;
		
		float aa_step(float t1, float t2, float f)
		{
			//return step(t2, f);
			return smoothstep(t1, t2, f);
		}
		
		void main(void)
		{
			float sdf = texture2D(u_texture, v_tex_coord).a;
			
			//float fw = fwidth(sdf)*0.5;
			float fw = length(vec2(dFdx(sdf), dFdy(sdf)))*0.5;
			
			float alpha = aa_step(0.5 - fw, 0.5 + fw, sdf);
			gl_FragColor = v_color*(v_color.a*alpha);
//			gl_FragColor = vec4(1, 0, 0, 1);
		}
	));
	
	program = LinkProgram(vshader, fshader);
	CHECK_GL_ERRORS();
	
//	GLint index = -1;//glGetUniformLocation(program, "u_texture");
//	glUniform1i(index, 0);
//	CHECK_GL_ERRORS();
	
	// Setu VBO and VAO.
#if __APPLE__
	glGenVertexArraysAPPLE(1, &vao);
	glBindVertexArrayAPPLE(vao);
#else
	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);
#endif
	
	glGenBuffers(1, &vbo);
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	
	SET_ATTRIBUTE(program, struct Vertex, vertex, GL_FLOAT);
	SET_ATTRIBUTE(program, struct Vertex, tex_coord, GL_FLOAT);
	SET_ATTRIBUTE(program, struct Vertex, color, GL_FLOAT);
	
	glBindBuffer(GL_ARRAY_BUFFER, 0);
#if __APPLE__
	glBindVertexArrayAPPLE(0);
#else
	glBindVertexArray(0);
#endif
	CHECK_GL_ERRORS();
	
	// Load the SDF font texture.
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, sdf_tex_width, sdf_tex_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, sdf_data);
	glGenerateMipmap(GL_TEXTURE_2D);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	CHECK_GL_ERRORS();
	
	// Fill in the glyph index array.
	for(int i=0; i<sdf_num_chars; i++){
		int char_index = sdf_spacing[i*8];
		glyph_indexes[char_index] = i;
	}
}

#undef MAX
#define MAX(__a__, __b__) (__a__ > __b__ ? __a__ : __b__)

static GLsizei triangle_capacity = 0;
static GLsizei triangle_count = 0;
static Triangle *triangle_buffer = NULL;

static Triangle *PushTriangles(GLsizei count)
{
	if(triangle_count + count > triangle_capacity){
		triangle_capacity += MAX(triangle_capacity, count);
		triangle_buffer = (Triangle *)realloc(triangle_buffer, triangle_capacity*sizeof(Triangle));
	}
	
	Triangle *buffer = triangle_buffer + triangle_count;
	triangle_count += count;
	return buffer;
}

static GLfloat
PushChar(int character, GLfloat x, GLfloat y, cpSpaceDebugColor color)
{
	int i = glyph_indexes[character];
	GLfloat w = (GLfloat)sdf_tex_width;
	GLfloat h = (GLfloat)sdf_tex_height;
	
	GLfloat gw = (GLfloat)sdf_spacing[i*8 + 3];
	GLfloat gh = (GLfloat)sdf_spacing[i*8 + 4];
	
	GLfloat txmin = sdf_spacing[i*8 + 1]/w;
	GLfloat tymin = sdf_spacing[i*8 + 2]/h;
	GLfloat txmax = txmin + gw/w;
	GLfloat tymax = tymin + gh/h;
	
	GLfloat s = Scale/scale_factor;
	GLfloat xmin = x + sdf_spacing[i*8 + 5]/scale_factor*Scale;
	GLfloat ymin = y + (sdf_spacing[i*8 + 6]/scale_factor - gh)*Scale;
	GLfloat xmax = xmin + gw*Scale;
	GLfloat ymax = ymin + gh*Scale;
	
	Vertex a = {{xmin, ymin}, {txmin, tymax}, color};
	Vertex b = {{xmin, ymax}, {txmin, tymin}, color};
	Vertex c = {{xmax, ymax}, {txmax, tymin}, color};
	Vertex d = {{xmax, ymin}, {txmax, tymax}, color};
	
	Triangle *triangles = PushTriangles(2);
	Triangle t0 = {a, b, c}; triangles[0] = t0;
	Triangle t1 = {a, c, d}; triangles[1] = t1;
	
	return sdf_spacing[i*8 + 7]*s;
}

#undef ChipmunkDemoTextDrawString

void
ChipmunkDemoTextDrawString(cpVect pos, char const *str)
{
	cpSpaceDebugColor c = LAColor(1.0f, 1.0f);
	GLfloat x = (GLfloat)pos.x, y = (GLfloat)pos.y;
	
	for(size_t i=0, len=strlen(str); i<len; i++){
		if(str[i] == '\n'){
			y -= LineHeight;
			x = (GLfloat)pos.x;
//		} else if(str[i] == '*'){ // print out the last demo key
//			glutBitmapCharacter(GLUT_BITMAP_HELVETICA_10, 'A' + demoCount - 1);
		} else {
			x += (GLfloat)PushChar(str[i], x, y, c);
		}
	}
}

void
ChipmunkDemoTextFlushRenderer(void)
{
//	triangle_count = 0;
//	ChipmunkDemoTextDrawString(cpv(-300, 0), "0.:,'");
	
	glBindBuffer(GL_ARRAY_BUFFER, vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(Triangle)*triangle_count, triangle_buffer, GL_STREAM_DRAW);
	
	glUseProgram(program);
	
#if __APPLE__
	glBindVertexArrayAPPLE(vao);
#else
	glBindVertexArray(vao);
#endif
	glDrawArrays(GL_TRIANGLES, 0, triangle_count*3);
		
	CHECK_GL_ERRORS();
}

void
ChipmunkDemoTextClearRenderer(void)
{
	triangle_count = 0;
}

static int pushed_triangle_count = 0;
void
ChipmunkDemoTextPushRenderer(void)
{
	pushed_triangle_count = triangle_count;
}

void
ChipmunkDemoTextPopRenderer(void)
{
	triangle_count = pushed_triangle_count;
}


